package tool

import java.io.File

import org.apache.commons.io.FileUtils
import tool.Pojo.{CompareFileInfo, ConfigData, DataFileInfo, FileInfo, GroupFileInfo, MyDataDir, MyMessage}

import scala.collection.mutable
import utils.Utils
import implicits.Implicits._

/**
 * Created by Administrator on 2019/9/12
 */
object FileTool {

  def metaboFileCheck(file: File): MyMessage = {
    val fileMessage = "数据文件"
    val lines = file.xlsxLines
    val headers = lines.head
    val repeatHeaders = headers.diff(headers.distinct)
    if (repeatHeaders.nonEmpty) {
      return MyMessage(false, s"数据文件表头 ${repeatHeaders.head} 重复!")
    }
    val hasHeaders = List("Index", "Name", "Standard.name", "Class", "HMDBID", "KeggID")
    val noExistHeaders = hasHeaders.diff(headers)
    if (noExistHeaders.nonEmpty) {
      return MyMessage(false, s"数据文件表头 ${noExistHeaders.head} 不存在!")
    }
    val repeatColumns = List("Index")
    val repeatMap = repeatColumns.map(x => (x, mutable.Set[String]())).toMap
    lines.drop(1).zipWithIndex.foreach { case (tmpColumns, i) =>
      val columns = tmpColumns.padTo(headers.size, "")
      if (columns.size > headers.size) {
        return MyMessage(false, s"${fileMessage}第${i + 2}行列数不正确,存在多余列!")
      }
      columns.zipWithIndex.foreach { case (column, j) =>
        val header = headers(j).toLowerCase
        if (repeatColumns.contains(header)) {
          if (repeatMap(header).contains(column)) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列重复!")
          } else repeatMap(header) += column
        }

      }
    }

    MyMessage(true, "")

  }

  def fileHeaderMerge(myTmpDir: MyDataDir)={
    val groupFile = myTmpDir.groupFile
    groupFile.csvLines.convertHeader(Map("sample" -> Tool.sampleIdColumnName)).toFile(groupFile)
    val sampleColorFileOp=myTmpDir.sampleColorFile
    sampleColorFileOp.foreach{file=>
      file.csvLines.convertHeader(Map("col" -> "color")).toFile(file)
    }
  }

  def fileCheck(myTmpDir: MyDataDir, configData: ConfigData) = {
    fileHeaderMerge(myTmpDir)
    val fileInfo = FileTool.getFileInfo(myTmpDir)
    var myMessage = FileTool.dataFileCheck(myTmpDir, configData, fileInfo)
    if (myMessage.valid) {
      myMessage = FileTool.groupFileCheck(myTmpDir, configData, fileInfo)
    }
    if (myMessage.valid) {
      myMessage = FileTool.compareFileCheck(myTmpDir, fileInfo)
    }
    if (myMessage.valid) {
      myTmpDir.sampleColorFile.foreach { x =>
        myMessage = FileTool.sampleColorFileCheck(myTmpDir, fileInfo)
      }
    }
    if (myMessage.valid) {
      myMessage = FileTool.corDirCheck(myTmpDir)
    }
    if (myMessage.valid) {
      myTmpDir.confounderFile.foreach { confounderFile =>
        myMessage = FileTool.confounderFileCheck(confounderFile)
      }
    }
    if (myMessage.valid) {
      myMessage = FileTool.summaryCheck(fileInfo)
    }
    myMessage
  }

  def getDataFileInfo(myTmpDir: MyDataDir) = {
    val dataFile = myTmpDir.dataFile
    val headers = dataFile.csvLines.head
    val sampleIds = headers.drop(1)
    DataFileInfo(sampleIds)
  }

  def dataFileCheck(myTmpDir: MyDataDir, configData: ConfigData, fileInfo: FileInfo): MyMessage = {
    val dataFile = myTmpDir.dataFile
    val fileMessage = "数据文件"
    val headers = dataFile.csvLines.head
    val lines = dataFile.csvLines
    val repeatHeaders = headers.diff(headers.distinct)
    if (repeatHeaders.nonEmpty) {
      return MyMessage(false, s"${fileMessage}表头 ${repeatHeaders.head} 重复!")
    }

    val metGroupMap = fileInfo.groupFileInfo.metGroupMap
    if (configData.isLoess == "T" && metGroupMap(headers.last) != "QC") {
      return MyMessage(false, s"${fileMessage}表头最后一列不为QC样，无法进行LOESS回归校正!")
    }

    headers.foreach { x =>
      if (!x.isValidRVar) {
        return MyMessage(false, s"${fileMessage}表头${x}不满足R变量的命名规则!")
      }
    }

    val repeatSet = mutable.Set[String]()
    lines.drop(1).zipWithIndex.foreach { case (tmpColumns, i) =>
      val columns = tmpColumns.padTo(headers.size, "")
      if (columns.size > headers.size) {
        return MyMessage(false, s"${fileMessage}第${i + 2}行列数不正确,存在多余列!")
      }
      columns.zipWithIndex.foreach { case (column, j) =>
        if (j == 0) {
          if (repeatSet.contains(column)) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列重复!")
          } else repeatSet += column
          if(column.isBlank){
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列为空!")
          }
        }

      }
    }
    MyMessage(true, "")
  }

  def getGroupFileInfo(myTmpDir: MyDataDir) = {
    val groupFile = myTmpDir.groupFile
    val metGroupMap = groupFile.csvLines.lineMap.map { map =>
      (map.getOrElse(Tool.sampleIdColumnNameLower, "NA"), map.getOrElse("classnote", "NA"))
    }.toMap
    val pairIdMap = groupFile.csvLines.lineMap.map { map =>
      (map.getOrElse(Tool.sampleIdColumnNameLower, "NA"), map.getOrElse("pair_id", "NA"))
    }.toMap
    GroupFileInfo(metGroupMap, pairIdMap)
  }

  def getCompareInfo(myTmpDir: MyDataDir) = {
    val file = myTmpDir.compareFile
    val map = file.csvLines.lineMap.map { map =>
      (map.getOrElse("compare", "NA").mySplit("_vs_"), map.getOrElse("ispaired", "NA") == "Y")
    }.toMap
    CompareFileInfo(map)
  }

  def getFileInfo(myTmpDir: MyDataDir) = {
    val groupFileInfo = FileTool.getGroupFileInfo(myTmpDir)
    val dataFileInfo = FileTool.getDataFileInfo(myTmpDir)
    val compareFileInfo = FileTool.getCompareInfo(myTmpDir)
    FileInfo(groupFileInfo, dataFileInfo, compareFileInfo)
  }

  def summaryCheck(fileInfo: FileInfo): MyMessage = {
    val metGroupMap = fileInfo.groupFileInfo.metGroupMap
    val groupMetMap = metGroupMap.reverses
    val pairIdMap = fileInfo.groupFileInfo.pairIdMap
    fileInfo.compareFileInfo.map.filter(x => x._1.size == 2 && x._2).foreach { case (groups, isPaired) =>
      val hasSameSize = groups.map { group =>
        group.mySplit("\\+").map(x => groupMetMap(x).size).sum
      }.toSet.size == 1
      if (!hasSameSize) {
        return MyMessage(false, s"组(${groups.mkString("、")}) 样本数不一致，无法进行配对检验!")
      } else {
        val groupPairsMap = groups.map { group =>
          val pairs = group.mySplit("\\+").flatMap { x =>
            groupMetMap(x).map { sampleId =>
              pairIdMap(sampleId)
            }
          }
          (group, pairs)
        }.toMap
        groupPairsMap.foreach { case (group, pairs) =>
          if (!pairs.isUniq) {
            return MyMessage(false, s"组${group} pair id 不唯一，无法进行配对检验!")
          }
        }
        val isSame = groupPairsMap.forall(_._2.toSet == groupPairsMap.head._2.toSet)
        if (!isSame) {
          return MyMessage(false, s"组(${groups.mkString("、")}) Pair ID 信息不一致，无法进行配对检验!")
        }

      }

    }
    MyMessage(true, "")
  }

  def groupFileCheck(myTmpDir: MyDataDir, configData: ConfigData, fileInfo: FileInfo): MyMessage = {
    val groupFile = myTmpDir.groupFile
    val fileMessage = "样品分组文件"
    val headers = groupFile.csvLines.head
    val lines = groupFile.csvLines
    val repeatHeaders = headers.diff(headers.distinct)
    if (repeatHeaders.nonEmpty) {
      return MyMessage(false, s"${fileMessage}表头 ${repeatHeaders.head} 重复!")
    }
    val hasHeaders = List(Tool.sampleIdColumnNameLower, "classNote")
    val headerMap = hasHeaders.map { header => (header.toLowerCase, header) }.toMap
    val noExistHeaders = headerMap.keyList.diff(headers.toLowerCase)
    if (noExistHeaders.nonEmpty) {
      return MyMessage(false, s"${fileMessage}表头 ${headerMap(noExistHeaders.head)} 不存在!")
    }

    val hasQc = groupFile.csvLines.selectOneColumn("classnote").contains("QC")
    if (!hasQc && configData.isLoess == "T") {
      return MyMessage(false, s"无QC样本，无法进行LOESS回归校正!")
    }

    val repeatColumns = List(Tool.sampleIdColumnNameLower)
    val repeatMap = repeatColumns.map(x => (x, mutable.Set[String]())).toMap
    lines.drop(1).zipWithIndex.foreach { case (tmpColumns, i) =>
      val columns = tmpColumns.padTo(headers.size, "")
      if (columns.size > headers.size) {
        return MyMessage(false, s"${fileMessage}第${i + 2}行列数不正确,存在多余列!")
      }
      columns.zipWithIndex.foreach { case (column, j) =>
        val header = headers(j).toLowerCase
        if (repeatColumns.contains(header)) {
          if (repeatMap(header).contains(column)) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列重复!")
          } else repeatMap(header) += column
        }
        if (header == Tool.sampleIdColumnNameLower) {
          if (!fileInfo.dataFileInfo.sampleIds.contains(column)) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列样品在数据文件中不存在!")
          }
        }
        if (header == "classnote") {
          if (!column.isValidRVar) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列组名格式不符合R变量命名要求!")
          }
          if (column.trim=="NA") {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列组名不能为NA!")
          }
        }
      }
    }
    MyMessage(true, "")
  }

  def compareFileCheck(myTmpDir: MyDataDir, fileInfo: FileInfo): MyMessage = {
    val groupFile = myTmpDir.compareFile
    val fileMessage = "比较配置文件"
    val headers = groupFile.csvLines.head
    val lowHeaders = headers.toLowerCase
    val lines = groupFile.csvLines
    val repeatHeaders = headers.diff(headers.distinct)
    if (repeatHeaders.nonEmpty) {
      return MyMessage(false, s"${fileMessage}表头 ${repeatHeaders.head} 重复!")
    }
    val hasHeaders = List("Compare", "isPaired")
    val headerMap = hasHeaders.map { header => (header.toLowerCase, header) }.toMap
    val noExistHeaders = headerMap.keyList.diff(headers.toLowerCase)
    if (noExistHeaders.nonEmpty) {
      return MyMessage(false, s"${fileMessage}表头 ${headerMap(noExistHeaders.head)} 不存在!")
    }
    val repeatColumns = List("compare")
    val repeatMap = repeatColumns.map(x => (x, mutable.Set[String]())).toMap
    lines.drop(1).zipWithIndex.foreach { case (tmpColumns, i) =>
      val columns = tmpColumns.padTo(headers.size, "")
      val lineMap = lowHeaders.zip(columns).toMap
      if (columns.size > headers.size) {
        return MyMessage(false, s"${fileMessage}第${i + 2}行列数不正确,存在多余列!")
      }
      columns.zipWithIndex.foreach { case (column, j) =>
        val header = headers(j).toLowerCase
        if (repeatColumns.contains(header)) {
          if (repeatMap(header).contains(column)) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列重复!")
          } else repeatMap(header) += column
        }
        if (header == "compare") {
          val groups = column.split("_vs_").flatMap(_.split("\\+"))
          val groupFileGroups = fileInfo.groupFileInfo.metGroupMap.values.toSet
          if (!groups.forall(x => groupFileGroups.contains(x))) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列在分组文件中不存在!")
          }
          if(groups.toList.nonUniq){
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列出现重复的vs组!")
          }
        }
        if (header.equalsIgnoreCase("isPaired")) {
          val groups = lineMap("compare").split("_vs_")
          val contents = List("Y", "N")
          if (groups.length == 2 && !contents.contains(column)) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列必须为(${contents.mkString("、")})中的一个!")
          }
        }

      }
    }
    MyMessage(true, "")

  }

  def sampleColorFileCheck(myTmpDir: MyDataDir, fileInfo: FileInfo): MyMessage = {
    val file = myTmpDir.sampleColorFile.get
    val fileMessage = "颜色配置文件"
    val headers = file.csvLines.head
    val lowHeaders = headers.toLowerCase
    val lines = file.csvLines
    val repeatHeaders = headers.diff(headers.distinct)
    if (repeatHeaders.nonEmpty) {
      return MyMessage(false, s"${fileMessage}表头 ${repeatHeaders.head} 重复!")
    }
    val hasHeaders = List("classNote", "color")
    val headerMap = hasHeaders.map { header => (header.toLowerCase, header) }.toMap
    val noExistHeaders = headerMap.keyList.diff(headers.toLowerCase)
    if (noExistHeaders.nonEmpty) {
      return MyMessage(false, s"${fileMessage}表头 ${headerMap(noExistHeaders.head)} 不存在!")
    }
    val repeatColumns = List("classnote")
    val repeatMap = repeatColumns.map(x => (x, mutable.Set[String]())).toMap
    val classNotes = lines.selectOneColumn("classnote")
    val groupFileGroups = fileInfo.groupFileInfo.metGroupMap.values.toSet
    val allGroups = groupFileGroups ++ fileInfo.compareFileInfo.map.keySet.flatten
    val noExistGroups = allGroups.diff(classNotes.toSet)
    if (noExistGroups.nonEmpty) {
      return MyMessage(false, s"${fileMessage}组${noExistGroups.head}不存在!")
    }
    lines.drop(1).zipWithIndex.foreach { case (tmpColumns, i) =>
      val columns = tmpColumns.padTo(headers.size, "")
      val lineMap = lowHeaders.zip(columns).toMap
      if (columns.size > headers.size) {
        return MyMessage(false, s"${fileMessage}第${i + 2}行列数不正确,存在多余列!")
      }
      columns.zipWithIndex.foreach { case (column, j) =>
        val header = headers(j).toLowerCase
        if (repeatColumns.contains(header)) {
          if (repeatMap(header).contains(column)) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列重复!")
          } else repeatMap(header) += column
        }
      }
    }
    MyMessage(true, "")

  }

  def repeatCheck(headers: Seq[String]) = {
    val repeatHeaders = headers.diff(headers.distinct)
    val valid = if (repeatHeaders.nonEmpty) {
      false
    } else true
    val headStr = repeatHeaders.headOption.getOrElse("")
    (valid, headStr)
  }

  def corDirCheck(myTmpDir: MyDataDir): MyMessage = {
    val dir = myTmpDir.corDir
    dir.listFiles().foreach { file =>
      val fileMessage = s"额外数据矩阵文件${file.getName}"
      val headers = file.csvLines.head
      val lowHeaders = headers.toLowerCase
      val lines = file.csvLines
      val (valid, headStr) = repeatCheck(headers)
      if (!valid) {
        return MyMessage(false, s"${fileMessage}表头 ${headStr} 重复!")
      }
      val repeatSet = mutable.Set[String]()
      lines.drop(1).zipWithIndex.foreach { case (tmpColumns, i) =>
        val columns = tmpColumns.padTo(headers.size, "")
        val lineMap = lowHeaders.zip(columns).toMap
        if (columns.size > headers.size) {
          return MyMessage(false, s"${fileMessage}第${i + 2}行列数不正确,存在多余列!")
        }
        columns.zipWithIndex.foreach { case (column, j) =>
          if (j == 0) {
            if (repeatSet.contains(column)) {
              return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列重复!")
            } else repeatSet += column
          } else {
            if (!(Utils.isDouble(column))) {
              return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列必须为数字!")
            }
          }

        }

      }
    }
    MyMessage(true, "")
  }

  def confounderFileCheck(confounderFile: File): MyMessage = {
    val file = confounderFile
    val fileMessage = s"confounder矩阵文件"
    val headers = file.csvLines.head
    val lowHeaders = headers.toLowerCase
    val lines = file.csvLines
    val (valid, headStr) = repeatCheck(headers)
    if (!valid) {
      return MyMessage(false, s"${fileMessage}表头 ${headStr} 重复!")
    }
    val repeatSet = mutable.Set[String]()
    lines.drop(1).zipWithIndex.foreach { case (tmpColumns, i) =>
      val columns = tmpColumns.padTo(headers.size, "")
      val lineMap = lowHeaders.zip(columns).toMap
      if (columns.size > headers.size) {
        return MyMessage(false, s"${fileMessage}第${i + 2}行列数不正确,存在多余列!")
      }
      columns.zipWithIndex.foreach { case (column, j) =>
        if (j == 0) {
          if (repeatSet.contains(column)) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列重复!")
          } else repeatSet += column
        } else {
          if (!(Utils.isDouble(column))) {
            return MyMessage(false, s"${fileMessage}第${i + 2}行第${j + 1}列必须为数字!")
          }
        }
      }
    }
    MyMessage(true, "")
  }

  def groupFileDeal(srcFile: File, destFile: File) = {
    val map = Map(Tool.sampleIdColumnNameLower -> "SampleID", "classnote" -> "ClassNote")
    srcFile.csvLines.convertHeader(map).toFile(destFile)
  }

  def sampleColorFileDeal(srcFile: Option[File], destFile: File) = {
    srcFile.foreach { file =>
      val map = Map("classnote" -> "ClassNote", "color" -> "col")
      file.csvLines.convertHeader(map).toFile(destFile)
    }


  }


}
